﻿using System;
using System.Collections;
using System.Collections.Generic;
using Me.Prettyprint.Cassandra.Connection;
using Me.Prettyprint.Cassandra.Serializers;
using Me.Prettyprint.Cassandra.Service;
using Me.Prettyprint.Cassandra.Utils;
using Me.Prettyprint.Hector.Api;
using Me.Prettyprint.Hector.Api.Beans;
using Me.Prettyprint.Hector.Api.Query;
using Org.Apache.Cassandra.Thrift;

namespace Me.Prettyprint.Cassandra.Model
{
    /// <summary>
    /// Default implementation for super columns. For an examplanation of what
    /// a super column is, please refer to these link.
    /// http://www.ebaytechblog.com/2012/07/16/cassandra-data-modeling-best-practices-part-1/#.U4McOShZh8E
    /// http://www.datastax.com/dev/blog/thrift-to-cql3
    /// 
    /// Note that super column is used by CQL3, but it's no longer called super
    /// columns.
    /// </summary>
    /// <typeparam name="SN"></typeparam>
    /// <typeparam name="N"></typeparam>
    /// <typeparam name="V"></typeparam>
    public class HSuperColumnImpl<SN, N, V> : HSuperColumn<SN, N, V>
    {
        public HSuperColumnImpl(SN sName, List<HColumn<N, V>> columns, long clock,
            Serializer<SN> sNameSerializer, Serializer<N> nameSerializer, Serializer<V> valueSerializer) : 
            this(sNameSerializer, nameSerializer, valueSerializer)
        {
            if (sName == null || columns == null)
            {
                throw new NullReferenceException("SuperName and Columns cannot be null");
            }
            Name = sName;
            Columns = columns;
            Clock = clock;
        }

        public HSuperColumnImpl(SuperColumn thriftSuperColumn, Serializer<SN> sNameSerializer,
            Serializer<N> nameSerializer, Serializer<V> valueSerializer)
            : this(sNameSerializer, nameSerializer, valueSerializer)
        {
            if (sNameSerializer == null || nameSerializer == null || valueSerializer == null)
            {
                throw new NullReferenceException("Serializers cannot be null");
            }
            Name = sNameSerializer.FromBytes(thriftSuperColumn.Name);
            Columns = FromThriftColumns(thriftSuperColumn.Columns);
        }
        protected HSuperColumnImpl(Serializer<SN> sNameSerializer, Serializer<N> nameSerializer,
            Serializer<V> valueSerializer)
        {
            if (sNameSerializer == null || nameSerializer == null)
            {
                throw new NullReferenceException("Serializers cannot be null");
            }
            SuperNameSerializer = sNameSerializer;
            NameSerializer = nameSerializer;
            ValueSerializer = valueSerializer;
        }
        public SN Name
        {
            get;set;
        }
        public List<HColumn<N,V>> Columns
        {
            get;set;
        }
        public long Clock
        {
            get;set;
        }
        public Serializer<SN> SuperNameSerializer
        {
            get;
            protected set;
        }
        protected Serializer<N> NameSerializer
        {
            get;set;
        }
        public Serializer<V> ValueSerializer
        {
            get;set;
        }
        public byte[] NameBytes
        {
            get;
            set;
        }
        public Serializer<SN> GetNameSerializer()
        {
            return SuperNameSerializer;
        }
        public HSuperColumn<SN, N, V> SetName(SN name)
        {
            if (name == null)
            {
                throw new NullReferenceException("Name cannot be null");
            }
            Name = name;
            return this;
        }
        public HSuperColumn<SN, N, V> SetSubcolumns(List<HColumn<N, V>> subcolumns)
        {
            if (subcolumns == null)
            {
                throw new NullReferenceException("Subcolumns cannot be null");
            }
            Columns = subcolumns;
            return this;
        }
        public HSuperColumn<SN, N, V> SetClock(long clock)
        {
            Clock = clock;
            return this;
        }
        public HColumn<N, V> Get(int i)
        {
            return Columns[i];
        }    
        public HColumn<N, V> GetSubColumnByName(N subColumnName)
        {
            foreach (HColumn<N,V> column in Columns ) 
            {
                if (NameSerializer.ToBytes(subColumnName).Equals(column.NameBytes) ) 
                {
                    return column;
                }
            }
            return null;
        }
        public byte[] GetNameBytes()
        {
            return SuperNameSerializer.ToBytes(Name);
        }
        public ByteBuffer GetNameByteBuffer()
        {
            return SuperNameSerializer.ToByteBuffer(Name);
        }
        public SuperColumn ToThrift()
        {
            if (Name == null || Columns == null)
            {
                return null;
            }
            SuperColumn supcol = new SuperColumn(SuperNameSerializer.ToBytes(Name),ToThriftColumn());
            return supcol;
        }
        private List<Column> ToThriftColumn()
        {
            List<Column> ret = new List<Column>(Columns.Count);
            foreach (HColumn<N, V> c in Columns)
            {
                ret.Add(((HColumnImpl<N, V>) c).ToThrift());
            }
            return ret;
        }
        private List<HColumn<N, V>> FromThriftColumns(List<Column> tcolumns)
        {
            List<HColumn<N, V>> cs = new List<HColumn<N,V>>(tcolumns.Count);
            foreach (Column c in tcolumns) 
            {
                cs.Add(new HColumnImpl<N, V>(c, NameSerializer, ValueSerializer));
            }
            return cs;
        }
        public override string ToString()
        {
            return string.Format("HSuperColumn({0},{1})", Name, Columns);
        }

    }
}
